
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <stdint.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <time.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <pthread.h>

#define FILE_PATH			"/kf/package/jpegsrc.v9a.tar.gz"

#define PACKAGE_TOTAL		1024U
#define PACKAGE_INFO		(4U * 3U)
#define PACKAGE_DATA		(PACKAGE_TOTAL - PACKAGE_INFO)

#define FILE_TOTAL			PACKAGE_DATA
#define FILE_INFO			(4U * 4U)
#define FILE_DATA			(FILE_TOTAL - FILE_INFO)

struct file_block
{
	uint32_t block_type;	// 0: 文件名（客户端发）	1: 有效文件块（客户端发）	2: 块请求（服务端发）		3:	控制命令(具体命令从data提取，4个字节)
	uint32_t block_id;
	uint32_t block_total;
	uint32_t valid_len;
	uint8_t data[FILE_DATA];		// 控制命令：	0：成功		1：成功，请返回文件校验码	2：失败
};

struct net_package
{
	uint32_t type;			// 0: 文件上传服务			1: 文件下载服务
	uint32_t valid_len;
	uint32_t checksum;
	uint8_t data[PACKAGE_DATA];
};

static uint32_t total_size = 0;
static uint32_t total_id = 0;

static uint32_t calc_checksum(void *data, uint32_t size)
{
	uint32_t i = 0, sum = 0xFFFFFFFF;
	for(i = 0; size > i; i += 4)
	{
		sum += *(unsigned char *)(data + i);
	}
	return sum;
}

static void file_block_statistics(uint32_t id, uint32_t total)
{
	float f_id = id * 1.0;
	float f_total = total * 1.0;
	printf("%f%%\n", f_id / f_total * 100);
}

static int file_block_cmd_parse(int socket)
{
	return -1;
}

static int file_block_send_block(int socket, uint32_t id)
{
	FILE *fp = NULL;
	struct file_block *block = NULL;
	struct net_package package;
	
	uint32_t offset = FILE_DATA * id;

	fp = fopen(FILE_PATH, "r");

	if(NULL != fp)
	{
		memset(&package, 0, PACKAGE_TOTAL);

		block = (struct file_block *)package.data;

		block->block_type = 1;
		block->block_id = id;
		block->block_total = total_id;
		block->valid_len = FILE_DATA < total_size - offset ? FILE_DATA : total_size - offset;

		fseek(fp, offset, SEEK_SET);
		fread(block->data, block->valid_len, 1, fp);
		fclose(fp);

		package.type = 0;
		package.valid_len = FILE_INFO + block->valid_len;
		package.checksum = calc_checksum(package.data, package.valid_len);

		write(socket, &package, package.valid_len + PACKAGE_INFO);
		
		if(0 < id && 0 == id % 50)
		{
			file_block_statistics(id, total_id);
		}

		return 0;
	}
	return -1;
}

static int file_block_parse(int socket, struct file_block *file, uint32_t len)
{
	int ret = 0;

	switch(file->block_type)
	{
		case 0:
			ret = -1;
			break;
		case 1:
			ret = -1;
			break;
		case 2:
			ret = file_block_send_block(socket, *(uint32_t *)file->data);
			break;
		case 3:
			ret = file_block_cmd_parse(socket);
			break;
		default:
			ret = -1;
			break;
	}
	return ret;
}

static int net_package_checksum(struct net_package *package)
{
	return package->checksum == calc_checksum(package->data, package->valid_len) ? 0 : -1;
}

static int net_package_parse(int socket, struct net_package *package)
{
	int ret = 0;

	if(0 != net_package_checksum(package))
	{
		printf("%s: checksum failed\n", __func__);
		return -1;
	}

	switch(package->type)
	{
		case 0:
			ret = file_block_parse(socket, (struct file_block *)package->data, package->valid_len);
			break;
		case 1:
			break;
		default:
			break;
	}
	return ret;
}

static long get_file_size(char *path)
{
	long filesize = 0;
	FILE *fp = NULL;
	fp = fopen(path, "r");
	if(NULL != fp)
	{
		fseek(fp, 0L, SEEK_END);
		filesize = ftell(fp);
		fclose(fp);
		return filesize;
	}
	return 0;
}

int main(int argc, char **argv)
{
	int ret = 0;

	int sockfd = 0;
	struct sockaddr_in remote_addr;
	
	long filesize = 0;

	struct net_package package;
	struct file_block *block = NULL;
	
	if(2 != argc)
	{
		return -1;
	}
	
	if(PACKAGE_TOTAL != sizeof(struct net_package))
	{
		return -1;
	}

	sockfd = socket(AF_INET, SOCK_STREAM, 0);

	memset(&remote_addr, 0, sizeof(remote_addr));

	remote_addr.sin_family = AF_INET;
	remote_addr.sin_port = htons(56032);
	remote_addr.sin_addr.s_addr = inet_addr(argv[1]);

	if(0 == connect(sockfd, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)))
	{
		filesize = get_file_size(FILE_PATH);

		if(0 < filesize && UINT32_MAX > (uint32_t)filesize)
		{
			memset(&package, 0, PACKAGE_TOTAL);

			total_size = (uint32_t)filesize;
			total_id = total_size / FILE_DATA + 1;

			block = (struct file_block *)package.data;

			block->block_type = 0;
			block->block_id = 0;
			block->block_total = total_id;
			snprintf((char *)block->data, FILE_DATA, "%s", "./jpegsrc.v9a.tar.gz");
			block->valid_len = strlen((char *)block->data);

			package.type = 0;
			package.valid_len = FILE_INFO + block->valid_len;
			package.checksum = calc_checksum(package.data, package.valid_len);
		
			write(sockfd, &package, package.valid_len + PACKAGE_INFO);
			
			while(1)
			{
				memset(&package, 0, PACKAGE_TOTAL);

				ret = read(sockfd, &package, PACKAGE_TOTAL);

				if(0 >= ret)
				{
					printf("%s: read failed!!!\n", __func__);
					ret = -1;
					break;
				}
				else
				{
					ret = net_package_parse(sockfd, &package);
					if(0 != ret)
					{
						ret = -1;
						break;
					}
				}
			}
		}
	}
	else
	{
		printf("connect failed\n");
	}

	close(sockfd);
	return ret;
}